package com.gitee.wsl.jvm.task.thread.executor


import com.gitee.wsl.func.Function
import com.gitee.wsl.jvm.task.thread.CPU_COUNT
import timber.log.Timber
import java.util.concurrent.ExecutorService
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong

typealias ThreadFactoryCreate = (para: Int) -> ExecutorService

class ThreadPoolExecutorCount(
    corePoolSize: Int,
    maximumPoolSize: Int,
    keepAliveTime: Long,
    unit: TimeUnit,
    val mWorkQueue: LinkedBlockingQueueCount,
    threadFactory: ThreadFactory
) : ThreadPoolExecutor(
    corePoolSize, maximumPoolSize,
    keepAliveTime, unit,
    mWorkQueue,
    threadFactory
) {
    private val mSubmittedCount = AtomicInteger()

    init {
        mWorkQueue.mPool = this
    }

    val submittedCount: Int
        get() = mSubmittedCount.get()

    override fun afterExecute(r: Runnable, t: Throwable) {
        mSubmittedCount.decrementAndGet()
        super.afterExecute(r, t)
    }

    override fun execute(command: Runnable) {
        if (this.isShutdown) return
        mSubmittedCount.incrementAndGet()
        try {
            super.execute(command)
        } catch (ignore: RejectedExecutionException) {
            Timber.tag("ThreadUtils").e("This will not happen!")
            mWorkQueue.offer(command)
        } catch (t: Throwable) {
            mSubmittedCount.decrementAndGet()
        }
    }

    companion object{
        val TYPE_CPU: ThreadFactoryCreate = { priority: Int->
            ThreadPoolExecutorCount(
                CPU_COUNT + 1, 2 * CPU_COUNT + 1,
                30, TimeUnit.SECONDS,
                LinkedBlockingQueueCount(mCapacity = 0),
                ThreadFactoryCount("cpu", priority)
            )
        }

        val TYPE_IO: ThreadFactoryCreate = { priority: Int ->
            ThreadPoolExecutorCount(
                2 * CPU_COUNT + 1, 2 * CPU_COUNT + 1,
                30, TimeUnit.SECONDS,
                LinkedBlockingQueueCount(),
                ThreadFactoryCount("io", priority)
            )
        }

        val TYPE_CACHED: ThreadFactoryCreate = { priority: Int ->
            ThreadPoolExecutorCount(
                0, 128,
                60L, TimeUnit.SECONDS,
                LinkedBlockingQueueCount(mCapacity = 0),
                ThreadFactoryCount("cached", priority)
            )
        }

        val TYPE_SINGLE: ThreadFactoryCreate = { priority: Int ->
            ThreadPoolExecutorCount(
                1, 1,
                0L, TimeUnit.MILLISECONDS,
                LinkedBlockingQueueCount(),
                ThreadFactoryCount("single", priority)
            )
        }

        fun TYPE_FIXED(poolSize:Int): ThreadFactoryCreate = { priority: Int ->
            ThreadPoolExecutorCount(
                poolSize, poolSize,
                0L, TimeUnit.MILLISECONDS,
                LinkedBlockingQueueCount(),
               ThreadFactoryCount("fixed($poolSize)", priority)
            )
        }
    }
}

class LinkedBlockingQueueCount(val mCapacity:Int = Int.MAX_VALUE) : LinkedBlockingQueue<Runnable>() {

    lateinit var mPool: ThreadPoolExecutor

    override fun offer(runnable: Runnable): Boolean {
        return if (mCapacity <= size  &&  mPool.poolSize < mPool.maximumPoolSize) {
            // create a non-core thread
            false
        } else super.offer(runnable)
    }
}


internal class ThreadFactoryCount @JvmOverloads constructor(
    val prefix: String="",
    val priority: Int=0,
    val isDaemon: Boolean = false,
    val namePrefix:String = prefix + "-pool-" + POOL_NUMBER.andIncrement + "-thread-"
) : ThreadFactory {
    private val count = AtomicLong()

    override fun newThread(r: Runnable): Thread {
        val t: Thread = object : Thread(r, namePrefix + count.andIncrement) {
            override fun run() {
                try {
                    super.run()
                } catch (t: Throwable) {
                    Timber.tag("ThreadUtils").e(t, "Request threw uncaught throwable")
                }
            }
        }
        t.isDaemon = isDaemon
        t.uncaughtExceptionHandler = Thread.UncaughtExceptionHandler { t, e -> println(e) }
        t.priority = priority
        return t
    }

    companion object {
        private val POOL_NUMBER = AtomicInteger(1)
    }

}